oo::class create Tcl {
    superclass Language

    method getEscapeChar {} { return \\ }
    method getQuoteTag {args} { return DoubleQuote }
    method getIndentChar {} {return \{}
    method getUnindentChar {} {return \}}
    method getTabSpacing {} {return "    "}
    method getIndentContinueSpacing {} {return "  "}

    method getVariableRelatedRegexp {} {
        # Varible
        return {::\w[\w:]*|\w[\w:]*|::\{[\w:]+\}|\{[\w:]+\}}
    }

    method getVariablePrefix {} {
        # Varible
        return \$
    }

    method isVariable? {word} {
        # Varible
        return ![string first [my getVariablePrefix] $word]
    }

    method isVariableDefinition? {prevWord curWord} {
        foreach v [my getVariableDefinitionWords]  {
            if {$v == $prevWord} { return 1 }
        }
        return 0
    }
    
    method getVariableDefinitionWords {} {
        # Varible
        return { set unset }
    }
    
    method isProcDefinition? {prevWord curWord} {
        foreach v [my getProcDefinitionWords]  {
            if {$v == $prevWord} { return 1 }
        }
        return 0
    }
    
    method getProcDefinitionWords {} {
        # Varible
        return { proc method }
    }

    method getKeyWordsGroup1 {} {
        # Varible
        return {
            after append array auto_execok auto_import auto_load
            auto_mkindex auto_mkindex_old auto_qualify auto_reset
            bgerror binary break
            case catch cd class clock close common concat constructor continue
            dde destructor
            else elseif encoding eof error eval exec exit expr
            fblocked fconfigure fcopy file fileevent filename flush for foreach format
            gets glob global
            history http
            if incr info inherit interp
            join
            lappend lindex linsert list llength load lrange lreplace lsearch lset lsort
            memory method msgcat
            namespace
            open
            package parray pid pkg::create pkg_mkIndex private proc protected public
            puts pwd
            re_syntax read regexp registry regsub rename resource return
            scan seek set socket source split string subst switch
            tcl_endOfWord tcl_findLibrary tcl_startOfNextWord tcl_startOfPreviousWord
            tcl_wordBreakAfter tcl_wordBreakBefore tcltest tclvars tell time trace
            unknown unset update uplevel upvar
            variable vwait
            while
        }
    }
    method getKeyWordsGroup2 {} {
        # Varible
        return {
            ErrorCode ErrorInfo
            auto_execs auto_index auto_noexec auto_noload auto_path
            env
            tcl_library tcl_nonwordchars tcl_patchLevel tcl_pkgPath tcl_platform
            tcl_precision tcl_rcFileName tcl_rcRsrcName tcl_version tcl_wordchars
            unknown_active
        }
    }
    method getKeyWordsGroup3 {} {
        # Varible
        return {
            bell bind bindtags bitmap button
            canvas checkbutton clipboard
            destroy
            entry event
            focus frame
            grab grid
            image
            label listbox lower
            menu menubutton message
            option
            pack photo place
            radiobutton raise
            scale scrollbar selection send
            text tk tk_bindForTraversal tk_bisque tk_chooseColor tk_dialog tk_focusFollowsMouse
            tk_focusNext tk_focusPrev tk_getOpenFile tk_getSaveFile tk_menuBar tk_messageBox
            tk_optionMenu tk_popup tk_setPalette tkerror tkvars tkwait toplevel
            winfo wm
        }
    }

    method getBlockEnd {textWidget index} {
        # Folder
        return [my getPairEnd $textWidget $index [list \{ \}]]
    }

    method getBlockHead {textWidget index} {
        # Folder
        return [my getPairHead $textWidget $index [list \{ \}]]
    }

    method respaceLineHead {textWidget index spacing} {
        # Indent
        set PadWidget [string trimleft $textWidget _]
        $PadWidget replace "$index linestart" \
            "$index linestart +[my getLineHeadSpacing $textWidget $index]c" $spacing
    }

    method updateIndent {textWidget index} {
        # Indent
        if [catch {
            set contexHead [my getPairHead $textWidget $index [list \{ \}]]
        } err] {
            # puts "err: $err"
            my respaceLineHead $textWidget $index {}
            return
        }
        # puts "head: $contexHead"
        if [catch {my getPairEnd $textWidget $index [list \{ \}]} contexEnd] {
            set contexEnd [$textWidget index end]
        }

        if {int($contexHead) == int($index)} { return }
        if {int($index) != 1 && \
                [regexp {\\\s*$} [$textWidget get "$index -1l linestart" "$index -1l lineend"]]} {
            if [catch {set headBracket [my getPairHead $textWidget $index [list \[ \]]]} err] {
                set spacing [string repeat " " [my getLineHeadSpacing $textWidget "$index -1l"]][my getIndentContinueSpacing]
            } else {
                if [$textWidget compare $headBracket < $contexHead] {
                    set contexHead $headBracket
                }

                if {int($contexHead) == int($index)-1} {
                    set spacing [string repeat " " [lindex [split $contexHead .] 1]][my getIndentContinueSpacing]
                } else {
                    set spacing [string repeat " " [my getLineHeadSpacing $textWidget "$index -1l"]][my getIndentContinueSpacing]
                }
            }
            # puts RUNTOHERE2
        } else {
            set spacing [string repeat " " [my getLineHeadSpacing $textWidget $contexHead]]
            if {int($index) != int($contexEnd)} {
                append spacing [my getTabSpacing]
            }
            # puts RUNTOHERE3
        }

        my respaceLineHead $textWidget $index $spacing
    }

    method updateIndentByTabEvent {textWidget index} {
        my updateIndent $textWidget $index
    }

    method updateComment {textWidget from to} {
        # Comment
        set index $from
        while {int($index) <= int($to)} {
            $textWidget tag remove Comment "$index linestart" "$index lineend"
            set content [$textWidget get \
                             "$index linestart" "$index lineend"]
            if {[set id [lindex [lindex [regexp -inline -indices {^(\s*|.*;\s*)\#} $content] 0] end]] != {}} {
                $textWidget tag add Comment "$index linestart +${id}c" "$index lineend"
            }
            set index [$textWidget index "$index +1l"]
            if {int($index) == int([$textWidget index end])} { break }
        }
    }
}